# Copyright (c) 2020 Presto Labs Pte. Ltd.
# Author: xguo, donggu

import datetime
import requests
import dateutil.parser

from coin2.exchange.product_info import ProductInfoDefault
from coin2.exchange.symbology import SymbologyInfo

def week_expiration(trading_datetime):
  date = trading_datetime.date()
  if date.weekday() < 4:
    expiry_date = date + datetime.timedelta(days=(4 - date.weekday()))
  elif date.weekday() > 4:
    expiry_date = date + datetime.timedelta(days=(4 - date.weekday() + 7))
  elif trading_datetime.hour < 12:
    expiry_date = date
  elif trading_datetime.hour >= 12:
    expiry_date = date + datetime.timedelta(days=7)

  return datetime.datetime(
      year=expiry_date.year,
      month=expiry_date.month,
      day=expiry_date.day,
      hour=12)


def next_week_expiration(trading_datetime):
  return week_expiration(week_expiration(trading_datetime) + datetime.timedelta(days=1))


def last_friday_of_month(year, month):
  if month == 12:
    next_month = datetime.datetime(
        year=year + 1,
        month=1,
        day=1,
        hour=12)
  else:
    next_month = datetime.datetime(
        year=year,
        month=month + 1,
        day=1,
        hour=12)
  this_month_last_day = next_month - datetime.timedelta(days=1)
  day = this_month_last_day
  while True:
    if day.weekday() == 4:
      break
    day = day - datetime.timedelta(days=1)
  return day


def quarter_expiration(trading_datetime):
  quarter_month = ((trading_datetime.month // 4 + 1) % 5) * 3
  this_quater_friday = last_friday_of_month(trading_datetime.year, quarter_month)
  if trading_datetime > this_quater_friday:
    if quarter_month == 12:
      return last_friday_of_month(trading_datetime.year + 1, 3)
    else:
      return last_friday_of_month(trading_datetime.year, quarter_month + 3)
  else:
    return this_quater_friday


def next_quarter_expiration(trading_datetime):
  return quarter_expiration(quarter_expiration(trading_datetime) + datetime.timedelta(days=1))


def nnext_quarter_expiration(trading_datetime):
  return quarter_expiration(next_quarter_expiration(trading_datetime) + datetime.timedelta(days=1))


def nnnext_quarter_expiration(trading_datetime):
  return quarter_expiration(nnext_quarter_expiration(trading_datetime) + datetime.timedelta(days=1))


def month_expiration(trading_datetime):
  friday = last_friday_of_month(trading_datetime.year, trading_datetime.month)
  if trading_datetime > friday:
    if trading_datetime.month == 12:
      return last_friday_of_month(trading_datetime.year + 1, 1)
    else:
      return last_friday_of_month(trading_datetime.year, trading_datetime.month + 1)
  else:
    return friday


def to_expire(info, expire_to_norm):
  if info['expiry'] is None:
    return "PERPETUAL"
  
  # month_code_arr = "FGHJKMNQUVXZ"  
  month_code_arr = "HMUZ"    # Mar Jun Sep Dec
  lable = info['symbol'][-3]
  if lable not in month_code_arr:
    return None

  expiration = dateutil.parser.parse(info["expiry"][:-5])
  for item in expire_to_norm:
    if expiration <= item:
      expiration = item
      break
  return expire_to_norm[expiration]


def get_delivery_type(now):
  quarter = quarter_expiration(now)
  next_quarter = next_quarter_expiration(now)
  nnext_quarter = nnext_quarter_expiration(now)
  nnnext_quarter = nnnext_quarter_expiration(now)

  expire_to_norm = {
      quarter: "QUARTER",
      next_quarter: "NEXT_QUARTER",
      nnext_quarter: "NNEXT_QUARTER",
      nnnext_quarter: "NNNEXT_QUARTER",
  }

  return expire_to_norm

def generate():
  mea = 'Futures.Bitmex.v1'
  me = 'Futures.Bitmex'
  default = ProductInfoDefault()
  symbol_info = SymbologyInfo()
  data = requests.get('https://www.bitmex.com/api/v1/instrument/active').json()

  expire_to_norm = get_delivery_type(datetime.datetime.utcnow())

  pis = []
  for info in data:
    if info["state"] == "Unlisted" or info["quoteCurrency"] == "XXX":
      print("[{}]filter native symbol :".format(mea, info['symbol']))
      continue

    native_symbol = info['symbol']
    native_base = info['underlying']
    native_quote = info['quoteCurrency']
    base = symbol_info.native_to_norm_currency(mea, native_base)
    quote = symbol_info.native_to_norm_currency(mea, native_quote)
    try:
      contract_type = to_expire(info, expire_to_norm)
      if contract_type is None: # Filter out items that do not trade
        continue

    except KeyError as e:
      print("[%s] ignore symbol: %s, expiry_date: %s" % (mea, native_symbol, e))
      continue
    symbol = "%s-%s.%s" % (base, quote, contract_type)

    if info['isQuanto']:
      adjust = 1e-8
      contract_value = float(abs(info['multiplier']) * adjust)
    else:
      if info['settlCurrency'] == 'USDt':
        adjust = 1e-6
      elif info['settlCurrency'] == 'XBt':
        adjust = 1e-8
      elif info['settlCurrency'] == 'Gwei':
        adjust = 1e-9
      elif info['settlCurrency'] == '':
        continue
      else:
        print("[%s] unknown setll currency: %s" % (mea, info['settlCurrency']))
      contract_value = float(abs(info['multiplier']) * adjust)

    if str(native_symbol).endswith("_ETH"):
      print("[{}]filter native symbol :{}".format(mea, native_symbol))
      continue


    pi = dict(
        symbol=symbol,
        base=base,
        quote=quote,
        native_symbol=native_symbol,
        native_base=native_base,
        native_quote=native_quote,
        price_ticksize=float(info['tickSize']),
        qty_ticksize=float(info['lotSize']),
        contract_value=contract_value,
        is_inverse=info['isInverse'],
    )
    pi = {**pi, **default.product_info(me, symbol)}
    pis.append(pi)

  pis = sorted(pis, key=lambda x: x['symbol'])
  return pis


def gen_coin1_constants():
  data = generate()
  data = convert_coin1_constants(data)
  return data


def convert_coin1_constants(data):
  symbol_list = [info['native_symbol'] for info in data]
  quote_currency_list = [info['native_quote'].upper() for info in data]
  base_currency_list = [info['native_base'].upper() for info in data]
  currency_list = base_currency_list + quote_currency_list
  res = {
      'coin_list': list(set(base_currency_list)),
      'fiat_list': list(set(quote_currency_list)),
  }
  return res


if __name__ == "__main__":
  import json
  print(json.dumps(generate(), indent=2))
